#include "ActionDecrypt.h"
#include "EncryptionManager.h"
#include "VectorUtil.h"
#include "qzip.h"
#include "Base64Encoder.h"
#include "SymmetricKeyFile.h"

#include <QDebug>
#include <QDate>
#include <QTime>
#include <QFile>
#include <vector>
#include <sstream>
#include <list>

using namespace kryptos::cryptography;
using namespace kryptos::util;
using namespace kryptos::entities;

namespace kryptos { namespace actions {

ActionDecrypt::ActionDecrypt(std::vector<unsigned char> selection,
                             SourceType sourceType,
                             kryptos::cryptography::PrivateKey privatekey,
                             SymmetricKeyInfoList symkeysinfo)
    : m_selection(selection)
    , m_sourceType(sourceType)
    , m_privateKey(privatekey)
    , m_symkeysinfo(symkeysinfo)
{
    Prepare();
}

ActionDecrypt::~ActionDecrypt()
{
    QFile::remove(m_compressedFileName.c_str());
}

bool ActionDecrypt::Prepare()
{
    return true;
}

bool ActionDecrypt::Cleanup()
{
    QFile::remove(m_decryptedFileTmp.c_str());
    QFile::remove(m_compressedFileName.c_str());

    return true;
}

ActionDecrypt::SourceType ActionDecrypt::sourceType()
{
    return m_sourceType;
}

void ActionDecrypt::RunAction()
{
    emit actionStarted();

    try
    {
        switch (m_sourceType)
        {
            case FILE:      decryptFile();
                            break;

            case SELECTION: decryptSelection();
                            break;
        }
    }
    catch (...)
    {
        //error signal
    }

    emit actionEnded(true);

    Cleanup();
}

void ActionDecrypt::decryptFile()
{
    QStringList files;
    files << QString(VectorUtil::toStringList(m_selection).front().c_str());

    std::string encryptedFile = files.first().toStdString();
    m_compressedFileName = encryptedFile + ".qz";

    SymmetricKeyFile symkey;
    EncryptionManager decrypter;

    if (decrypter.cryptoFormat(encryptedFile) == CryptoFile::K_OTPRSA01)
    {
        std::string symkeyid = decrypter.symmetricKeyNeeded(encryptedFile, m_privateKey);
        SymmetricKeyInfo info = findSymmetricKeyInfo(symkeyid);
        symkey.change(info.keyfile());
    }

    decrypter.decrypt(encryptedFile, m_compressedFileName, m_privateKey, symkey);

    qzip::QZip::unzip(m_compressedFileName.c_str(), QFileInfo(files.first()).canonicalPath());
}

void ActionDecrypt::decryptSelection()
{
    m_decryptedFileTmp   = QDir::current().canonicalPath().toStdString() + "//" + QDate::currentDate().toString("yy-MM-dd").toStdString() + QTime::currentTime().toString("hh-mm-ss").toStdString() + ".k";
    m_compressedFileName = m_decryptedFileTmp.substr(0, m_decryptedFileTmp.find(".k")).append(".qz");

    QFile tempFile(m_decryptedFileTmp.c_str());
    tempFile.open(QIODevice::ReadWrite);
    tempFile.write((char*)&m_selection[0], m_selection.size());
    tempFile.flush();
    tempFile.close();

    SymmetricKeyFile symkey;
    EncryptionManager decrypter;

    if (decrypter.cryptoFormat(m_decryptedFileTmp) == CryptoFile::K_OTPRSA01)
    {
        std::string symkeyid = decrypter.symmetricKeyNeeded(m_decryptedFileTmp, m_privateKey);
        SymmetricKeyInfo info = findSymmetricKeyInfo(symkeyid);
        symkey.change(info.keyfile());
    }

    decrypter.decrypt(m_decryptedFileTmp, m_compressedFileName, m_privateKey, symkey);

    qzip::QZip::unzip(m_compressedFileName.c_str(), QDir::current().canonicalPath());

    // saves decrypted selection in buffer
    QString uncompressedFileName = QDir::current().canonicalPath() + "/" + qzip::QZip::zippedFiles(m_compressedFileName.c_str()).first();
    QFile tempUncompressedFile(uncompressedFileName);
    tempUncompressedFile.open(QIODevice::ReadOnly);

    m_decryptedSelection = QString(tempUncompressedFile.readAll()).toStdString();

    QFile::remove(uncompressedFileName);
}

std::string ActionDecrypt::decryptedSelection()
{
    return m_decryptedSelection;
}

SymmetricKeyInfo ActionDecrypt::findSymmetricKeyInfo(const std::string& id)
{
    SymmetricKeyInfo syminfo;

    foreach(const SymmetricKeyInfo& info, m_symkeysinfo)
    {
        if (info.id() == id)
        {
            syminfo = info;
            break;
        }
    }

    return syminfo;
}

}}
